package com.clx.chat.handler;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.clx.chat.controller.RabbitmqSender;
import com.clx.chat.mapper.ContactMapper;
import com.clx.chat.mapper.FileMapper;
import com.clx.chat.mapper.MessageMapper;
import com.clx.common.domain.entity.Contact;
import com.clx.common.domain.entity.DiskFile;
import com.clx.common.domain.entity.Message;
import com.clx.common.utils.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


@Component
@ChannelHandler.Sharable//不太懂，意思是可共享,每一个用户进来都会有一个自己的通道，线程安全
@Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Autowired
    private RabbitmqSender rabbitmqSender;

    @Autowired
    private MessageMapper messageMapper;

    @Autowired
    private ContactMapper contactMapper;

    @Autowired
    private FileMapper fileMapper;

    //存储用户与通道
    private static final ConcurrentHashMap<Integer, Channel> userChannelMap = new ConcurrentHashMap<>();


    /**
     * 客户端建立连接调用
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 当有新的用户连接时，可以在后续步骤中获取用户 ID 并建立映射
        //注意这里sa-token获取不了spring web的上下文，所以不能直接获取loginId
        log.info("新客户端连接: {}", ctx.channel().id());
        super.channelActive(ctx);
    }
    /**
     * 接受客户端发送的消息
     * @param ctx
     * @param msg
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        String content = msg.text();
        JSONObject jsonContent = JSON.parseObject(content);
        if (ObjectUtils.isEmpty(jsonContent)){
            throw new RuntimeException("客户端发送的消息为：null或者空");
        }
        String type =  jsonContent.getString("type");
        //判断是用户id还是用户消息
        if (StringUtils.equals(type, "user_Id")) {
            //存储用户id
            Integer userId = jsonContent.getInteger("userId");
            userChannelMap.put(userId,ctx.channel());
            return;
        }else if (StringUtils.equals(type,"user_message")){
            //用户之间发送文本消息
            Integer senderId = jsonContent.getInteger("senderId");
            Integer receiverId = jsonContent.getInteger("receiverId");
            String text = (String) jsonContent.get("text");
            //判断两个用户是否在线
            if (userChannelMap.containsKey(senderId) && userChannelMap.containsKey(receiverId)) {
                //在线,通过netty进行实时通信
                //存储数据库
                Message message = saveMessageAndContact(senderId, receiverId, text);
                String s = JSON.toJSONString(message);
                //返回消息到客户端
                Channel channelOther = userChannelMap.get(receiverId);
                Channel channelSelf = userChannelMap.get(senderId);
                channelSelf.writeAndFlush(new TextWebSocketFrame(s));
                channelOther.writeAndFlush(new TextWebSocketFrame(s));
                return;
            } else {
                //对方用户不在线,把消息发送到rabbitmq,暂时性持久化存储
                Map<String,String> temp=new HashMap<>();
                temp.put("sendId",senderId.toString());
                temp.put("receiverId",receiverId.toString());
                temp.put("text",text);
                rabbitmqSender.sendMessage(temp);
            }
        }else if (StringUtils.equals(type,"send_file")){
            Integer receiverId = jsonContent.getInteger("receiverId");
            //存储返回格式消息
            //用户互发文件
            JSONArray temps = jsonContent.getJSONArray("fileList");
            List<DiskFile> fileList = temps.toJavaList(DiskFile.class);
            if (ObjectUtils.isEmpty(fileList)){
                throw new RuntimeException("发送的文件记录为空");
            }
            //存储所有文件id
            List<Long> fileIdList=new ArrayList<>();
            Long senderId=null;
            for (DiskFile file : fileList) {
                fileIdList.add(file.getId());
                senderId=file.getUserId();
            }
            //查询数据库,判断对方用户是否已经拥有这些文件
            List<DiskFile> haveFile = fileMapper.isHaveFile(fileIdList, Long.valueOf(receiverId));
            System.out.println(haveFile);
            if (ObjectUtils.isEmpty(haveFile)){
                //不存在 把文件记录拷贝一份给接受的用户
                for (DiskFile file : fileList) {
                    file.setId(null);
                    file.setUserId(Long.valueOf(receiverId));
                    if (fileMapper.insertFile(file)<0) {
                        throw new RuntimeException("发送文件失败");

                    }
                }
                //判断两个用户是否在线
                String text="成功发送"+fileList.size()+"个文件";
                if (userChannelMap.containsKey(senderId) && userChannelMap.containsKey(receiverId)) {
                    //在线,通过netty进行实时通信
                    //存储数据库
                    Message message = saveMessageAndContact(Math.toIntExact(senderId), receiverId, text);
                    String s = JSON.toJSONString(message);
                    //返回消息到客户端
                    Channel channelOther = userChannelMap.get(receiverId);
                    Channel channelSelf = userChannelMap.get(senderId);
                    channelSelf.writeAndFlush(new TextWebSocketFrame(s));
                    channelOther.writeAndFlush(new TextWebSocketFrame(s));
                    return;
                } else {
                    //对方用户不在线,把消息发送到rabbitmq,暂时性持久化存储
                    Map<String,String> temp=new HashMap<>();
                    temp.put("sendId",senderId.toString());
                    temp.put("receiverId",receiverId.toString());
                    temp.put("text",text);
                    rabbitmqSender.sendMessage(temp);
                }
            }

        }



    }

    /**
     * 客户端断开连接调用
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 当用户断开连接时，从映射中移除该用户的信息
        for (Integer userId : userChannelMap.keySet()) {
            if (userChannelMap.get(userId) == ctx.channel()) {
                userChannelMap.remove(userId);
                log.info("用户 {} 已断开连接", userId);
                break;
            }
        }
        super.channelInactive(ctx);
    }


    /**
     * 获取对应userId的websocket的channel通道
     */
    public static Channel getChanelByUserId(Integer userId){
        return userChannelMap.get(userId);
    }


    /**
     * 获取用户的在线状态
     */
    public static boolean getOnlineStatus(Integer userId){
        return userChannelMap.containsKey(userId);
    }

    /**
     * 存储message和contact到数据库
     * @param senderId
     * @param receiverId
     * @param text
     * @return
     */
    @Transactional
    public Message saveMessageAndContact(Integer senderId,Integer receiverId,String text){
        try {
            //根据recriverId查询concat表
            Contact contact = contactMapper.selectByuser(senderId, receiverId);
            Contact contact2 = contactMapper.selectByuser(receiverId, senderId);
            //同时更新两个用户联系人的lastmessage
            contact.setLastMessage(text);
            contact2.setLastMessage(text);
            contactMapper.updateContact(contact);
            contactMapper.updateContact(contact2);
            //存储message到数据库
            Message message = Message.builder()
                    .type(1)
                    .status(0)
                    .content(text)
                    .senderId(senderId)
                    .receiverId(receiverId)
                    .contactId(contact.getId())
                    .isRead(0)
                    .build();
            messageMapper.insertMessage(message);
            return message;
        } catch (Exception e) {
            log.error("存储聊天消息失败：",e);
            throw new RuntimeException(e);
        }
    }


    /**
     * 用户上线检查是否有离线消息
     * @param userId
     */
    public void isHaveNotOnlineMessage(Integer userId) {
        // 检查RabbitMQ队列中是否有未发送的消息
       org.springframework.amqp.core.Message message = rabbitmqSender.isHaveNotOnline();
        while (message != null) {
            Message tempMessage= null;
            try {
                tempMessage = new ObjectMapper().readValue(message.getBody(), Message.class);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            Integer receiverId = tempMessage.getReceiverId();
            Integer senderId = tempMessage.getSenderId();
            String s = JSON.toJSONString(tempMessage);
            if (receiverId == userId) {
                // 发送消息给用户
                Channel channel = NettyServerHandler.getChanelByUserId(receiverId);
                Channel channel1 = NettyServerHandler.getChanelByUserId(senderId);
                channel.writeAndFlush(new TextWebSocketFrame(s));
                channel1.writeAndFlush(new TextWebSocketFrame(s));
                // 手动确认消息
                try {
                    rabbitmqSender.ackMessage(message);
                } catch (Exception e) {
                    throw new RuntimeException("消息确认失败", e);
                }
            }
            // 继续检查下一条消息
            message =  rabbitmqSender.isHaveNotOnline();
        }
    }
}